package com.tangula.web.kashine.business;

import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;

import com.tangula.core.Logging;
import com.tangula.web.form.DefaultPagingBody;
import com.tangula.web.kashine.entity.AccountEntity;
import com.tangula.web.kashine.entity.AwardEntity;
import com.tangula.web.kashine.entity.ClassStudentRefEntity;
import com.tangula.web.kashine.entity.FreeTeacherModule;
import com.tangula.web.kashine.entity.HistoryEntity;
import com.tangula.web.kashine.entity.LoadOrgTeacherForm;
import com.tangula.web.kashine.entity.OrgStudentRefEntity;
import com.tangula.web.kashine.entity.PagableForm;
import com.tangula.web.kashine.entity.SendMoneyForm;
import com.tangula.web.kashine.entity.StudentAwardRecordEntity;
import com.tangula.web.kashine.entity.TeacherForm;
import com.tangula.web.kashine.entity.TeacherPageableForm;
import com.tangula.web.kashine.entity.TeacherSendMoneyForm;
import com.tangula.web.kashine.repository.AccountEntityRepository;
import com.tangula.web.kashine.repository.AwardEntityRepository;
import com.tangula.web.kashine.repository.ClassStudentRefEntityRepository;
import com.tangula.web.kashine.repository.HistoryEntityRepository;
import com.tangula.web.kashine.repository.OrgStudentRefEntityRepository;
import com.tangula.web.kashine.repository.StudentAwardRecordEntityRepository;
import com.tangula.web.kashine.repository.StudentOrganizationRefRepository;

@Service
public class TeacherBusinessImpl implements TeacherBusiness, Logging {
	@Autowired
	AccountEntityRepository accRepo;
	@Autowired
	AwardEntityRepository awRepo;
	@Autowired
	StudentOrganizationRefRepository studentOrgRepository;
	@Autowired
	HistoryEntityRepository hisRepo;

	@Autowired
	StudentAwardRecordEntityRepository stuAwardRepo;

	@Autowired
	ClassStudentRefEntityRepository classStuRefRepo;

	@Autowired
	OrgStudentRefEntityRepository orgStuRepo;

	public DefaultPagingBody<AccountEntity> loadOrgTeachers(LoadOrgTeacherForm loadOrgTeacherForm) {
		if (loadOrgTeacherForm != null) {
			DefaultPagingBody<AccountEntity> d = new DefaultPagingBody<>(loadOrgTeacherForm.getIndex(),
					loadOrgTeacherForm.getSize(),
					accRepo.findByParentOrgOfTeacherAndTypeAndBlock(loadOrgTeacherForm.getCondition(), "teacher", "y")
							.size(),
					accRepo.findByParentOrgOfTeacherAndTypeAndBlock(loadOrgTeacherForm.getCondition(), "teacher", "y",
							PageRequest.of(loadOrgTeacherForm.getIndex() - 1, loadOrgTeacherForm.getSize(),
									Sort.by(new Sort.Order(Direction.ASC, "id")))));
			return d;
		} else {
			return null;
		}
	}

	@Override
	public AccountEntity register(AccountEntity account) {
		if (account != null) {
			if (accRepo.findByEmail(account.getEmail()) != null) {
				return null;
			} else {
				account.setBlock("y");
				return accRepo.saveAndFlush(account);
			}
		} else {
			return null;
		}
	}

	@Override
	public Boolean sendMoneyToStudent(SendMoneyForm send) throws BusinessFailException {

		if (send != null) {
			String amount_text = send.getAmount();
			String teacher_id = send.getTeacherId();
			String student_id = send.getStudentId();
			if (StringUtils.isAnyBlank(teacher_id, student_id) || !StringUtils.isNumeric(amount_text)) {
				//logInfo("teacher send money fail: "+teacher_id+","+student_id+","+amount_text);
				throw new BusinessFailException("Teacher or student not found, or amount is not a number!");
			}

			int amount = Integer.parseInt(send.getAmount());
			if (amount <= 0) {
				//logInfo("teacher send money fail: "+teacher_id+","+student_id+","+amount_text+" is less than zero");
				throw new BusinessFailException("Amount is less than zero!");
			}

			AccountEntity teacher = accRepo.findById(send.getTeacherId()).orElse(null);
			AccountEntity student = accRepo.findById(send.getStudentId()).orElse(null);

			if (teacher == null || student == null || teacher.getMoney() < amount) {
				//logInfo("teacher send money fail: "+teacher_id+","+student_id+","+amount+" and teacher's money "+teacher.getMoney()+" is less then amount.");
				throw new BusinessFailException("You have not enough money, your amount is "+teacher.getMoney()+"!");
			} else {

				OrgStudentRefEntity ref = orgStuRepo.findByOrgIdAndStudentId(teacher.getParentOrgOfTeacher(),
						student.getId());

				if (ref == null) {
					logInfo("teacher send money fail: "+teacher_id+","+student_id+","+amount+" and student is not in org.");
					throw new BusinessFailException("The student "+student.getName()+" is not a member of your organization!");
				}

				teacher.setMoney(teacher.getMoney() - amount);
				student.setMoney(student.getMoney() + amount);

				String tchr_name = teacher.getName();
				String stdt_name = student.getName();

				ref.setMoney(new BigInteger(ref.getMoney()).add(new BigInteger(send.getAmount())).toString());
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				HistoryEntity teahis = new HistoryEntity();
				teahis.setDate(sdf.format(new Date()));
				teahis.setHistoryDes("send money to " + student.getName());
				teahis.setMoney(amount);
				teahis.setUserId(teacher_id);
				teahis.setUserName(tchr_name);
				teahis.setResavId(student_id);
				teahis.setRefName(stdt_name);
				teahis.setChange("-");

				HistoryEntity stuhis = new HistoryEntity();
				stuhis.setDate(sdf.format(new Date()));
				stuhis.setHistoryDes("receive money from " + teacher.getName());
				stuhis.setMoney(amount);
				stuhis.setUserId(student_id);
				stuhis.setUserName(stdt_name);

				stuhis.setChange("+");
				stuhis.setResavId(teacher_id);
				stuhis.setRefName(tchr_name);

				hisRepo.save(teahis);
				hisRepo.save(stuhis);
				orgStuRepo.save(ref);
				accRepo.save(teacher);
				accRepo.save(student);

				hisRepo.flush();
				orgStuRepo.flush();
				accRepo.flush();

				return true;
			}
		} else {
			return true;
		}
	}

	@Override
	public DefaultPagingBody<TeacherSendMoneyForm> showHistory(TeacherPageableForm teacher) {
		DefaultPagingBody<TeacherSendMoneyForm> res = new DefaultPagingBody<>(teacher.getIndex(), teacher.getSize());
		if (teacher != null) {
			Page<HistoryEntity> page = hisRepo.findByUserId(teacher.getCondition(),
					teacher.toPageRequest(Sort.Order.desc("date")));

			res.setTotal((int) page.getTotalElements());
			List<TeacherSendMoneyForm> items = page.getContent().stream().map(it -> {
				TeacherSendMoneyForm tsm = new TeacherSendMoneyForm();
				tsm.setAmount("$" + it.getMoney());
				tsm.setId(it.getId());
				tsm.setName(it.getRefName());
				String date = it.getDate();
				if (date == null) {
					date = "";
				}
				if (date.contains(" ")) {
					date = date.split(" ")[0];
				}
				tsm.setTime(date);
				return tsm;
			}).collect(Collectors.toList());
			res.setItems(items);

		}
		return res;
	}

	@Override
	public DefaultPagingBody<AccountEntity> loadTeachers(PagableForm form) {
		DefaultPagingBody<AccountEntity> res = new DefaultPagingBody<>(form);
		if (form != null) {
			Page<AccountEntity> cp = accRepo.findByType("TEACHER",
					form.toPageRequest(new Sort.Order(Direction.DESC, "id")));
			res.fill(cp);
		}
		return res;
	}

	@Override
	public List<AccountEntity> showTeacher(PagableForm pag) {
		if (pag != null) {
			return accRepo.findByTypeAndBlock("teacher", "y",
					PageRequest.of(pag.getIndex() - 1, pag.getSize(), Sort.by(new Sort.Order(Direction.ASC, "id"))));
		} else {
			return null;
		}
	}

	@Override
	public AccountEntity seleteTeacher(TeacherForm teacher) {
		if (teacher != null) {
			return accRepo.findById(teacher.getCondition()).get();
		} else {
			return null;
		}
	}

	@Override
	public AccountEntity blockTeacher(TeacherForm teacher) {
		if (teacher != null) {
			AccountEntity account = accRepo.findById(teacher.getCondition()).get();
			account.setBlock("n");
			return accRepo.saveAndFlush(account);
		} else {
			return null;
		}
	}

	@Override
	public AccountEntity unblockTeacher(TeacherForm teacher) {
		if (teacher != null) {
			AccountEntity account = accRepo.findById(teacher.getCondition()).get();
			account.setBlock("y");
			return accRepo.saveAndFlush(account);
		} else {
			return null;
		}
	}

	@Override
	public AccountEntity orgRegister(AccountEntity account) {
		if (account != null) {
			if (accRepo.findByEmail(account.getEmail()) == null) {
				account.setBlock("y");
				return accRepo.saveAndFlush(account);
			} else {
				return null;
			}
		} else {
			return null;
		}
	}

	@Override
	public AccountEntity blockOrUnblockTeacher(TeacherForm teacher) {
		if (teacher != null) {
			return accRepo.findById(teacher.getCondition()).get();
		} else {
			return null;
		}

	}

	@Override
	public DefaultPagingBody<FreeTeacherModule> loadFreeTeacher(PagableForm pag) {
		if (pag != null) {
			List<AccountEntity> lists = accRepo.findByTypeAndBlock("TEACHER", "y",
					PageRequest.of(pag.getIndex() - 1, pag.getSize(), Sort.by(new Sort.Order(Direction.ASC, "id"))));
			List<FreeTeacherModule> free = new ArrayList<FreeTeacherModule>();
			for (AccountEntity list : lists) {
				if (StringUtils.isEmpty(list.getParentOrgOfTeacher())) {
					FreeTeacherModule f = new FreeTeacherModule();
					f.setTeacherId(list.getId());
					f.setTeacherName(list.getName());
					f.setTeacherIcon(list.getImage());
					free.add(f);
				}
			}
			DefaultPagingBody<FreeTeacherModule> freeTeacher = new DefaultPagingBody<>(pag.getIndex(), pag.getSize(),
					free.size(), free);
			return freeTeacher;
		} else {
			return null;
		}
	}

	@Override
	public boolean block(String id) {
		AccountEntity entity = accRepo.findById(id).orElse(null);

		if (entity != null) {
			entity.setBlock("n");
			accRepo.saveAndFlush(entity);
		}

		return true;
	}

	@Override
	public boolean unblock(String id) {
		AccountEntity entity = accRepo.findById(id).orElse(null);

		if (entity != null) {
			entity.setBlock("y");
			accRepo.saveAndFlush(entity);
		}

		return true;
	}

	@Override
	public AccountEntity loadTeacher(String id) {
		AccountEntity entity = accRepo.findById(id).orElse(null);
		if (entity != null) {
			entity.setPassword("********");
		}
		return entity;
	}

	@Override
	public int countByOrgId(String id) {
		int res = 0;
		res = accRepo.countByParentOrgOfTeacher(id);
		return res;
	}

	@Override
	public Page<AwardEntity> loadAllAwards(String id, PageRequest pr) {
		return awRepo.findByOwnerTeacher(id, pr);
	}

	@Override
	public boolean createAward(AwardEntity entity) {
		awRepo.saveAndFlush(entity);
		return true;
	}

	@Override
	public Page<AccountEntity> loadOutOfClassStudent(String teacherId, String classId, Pageable pagination) {
		return accRepo.findOutofClassStudent(teacherId, classId, pagination);
	}

	@Override
	public boolean putStudent2Class(String studentId, String classId) {
		ClassStudentRefEntity entity = new ClassStudentRefEntity();
		entity.setClassId(classId);
		entity.setStudentId(studentId);

		accRepo.findById(studentId).ifPresent(stu -> {
			entity.setStudentDesc(stu.getAccountDesc());
			entity.setStudentImage(stu.getImage());
			entity.setStudentName(stu.getName());
			classStuRefRepo.saveAndFlush(entity);
		});

		return true;
	}

	@Override
	public Page<ClassStudentRefEntity> loadClassStudents(String classId, Pageable pg) {
		return classStuRefRepo.findByClassId(classId, pg);
	}

	@Override
	public List<ClassStudentRefEntity> loadAllClassMembers(String classId) {
		return classStuRefRepo.findByClassId(classId);
	}

	@Override
	public boolean sendAwards2Student(String teacherId, String studentId, List<String> awards) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		List<StudentAwardRecordEntity> entities = new ArrayList<>(awards.size());
		for (String awid : awards) {
			awRepo.findById(awid).ifPresent(aw -> {
				StudentAwardRecordEntity cur = new StudentAwardRecordEntity();
				entities.add(cur);
				cur.setAwardDesc(aw.getAwardDesc());
				cur.setAwardImage(aw.getAwardIcon());
				cur.setAwardName(aw.getAwardName());
				cur.setOwnerTeacherId(teacherId);
				cur.setStudentId(studentId);
				cur.setSendTime(sdf.format(new Date()));
			});
		}
		stuAwardRepo.saveAll(entities);
		stuAwardRepo.flush();

		return true;
	}

	@Override
	public Page<AccountEntity> loadAllStudent(String teacherId, Pageable pr) {
		return accRepo.queryAllStudentOfTeacher(teacherId, pr);
	}
}
